<template>
  <div class="v-input-number" :class="[{ disabled: disabled }]">
    <span
      role="button"
      @click="decrease"
      class="v-input_btn decrease flex-center"
      :class="[
        {
          disabled: minDisabled,
        },
      ]"
      >-</span
    >
    <span
      role="button"
      @click="increase"
      class="v-input_btn increase flex-center"
      :class="[
        {
          disabled: maxDisabled,
        },
      ]"
      >+</span
    >
    <v-input
      :value="displayValue"
      :disabled="disabled"
      :placeHolder="placeHolder"
      type="number"
      @blur="handleBlur"
      @focus="handleFocus"
      @input="handlerInput"
      @change="handleInputChange"
      @keydown.up.native.prevent="increase"
      @keydown.down.native.prevent="decrease"
    ></v-input>
  </div>
</template>
<script>
import VInput from "../input/input.vue";
export default {
  name: "VInputNumber",
  components: {
    VInput,
  },
  data() {
    return {
      userInput: null,
      currentValue: 0,
    };
  },
  props: {
    value: {},
    placeHolder: String,
    disabled: {
      type: Boolean,
      default: false,
    },
    step: {
      type: Number,
      default: 1,
    },
    precision: Number,
    max: {
      type: Number,
      default: Infinity,
    },
    min: {
      type: Number,
      default: -Infinity,
    },
  },
  watch: {
    value(newVal) {
      this.setCurrentValue(newVal);
    },
  },
  computed: {
    minDisabled() {
      return this.disabled || this.currentValue <= this.min;
    },
    maxDisabled() {
      return this.disabled || this.currentValue >= this.max;
    },
    displayValue() {
      if (this.userInput !== null) {
        return this.userInput;
      }
      let currentValue = this.value;
      if (currentValue === undefined) {
        currentValue = this.min || 0;
      } else {
        currentValue = currentValue >= this.min ? currentValue : this.min;
      }
      if (this.precision !== undefined) {
        currentValue = currentValue.toFixed(this.precision);
      }
      return currentValue;
    },
  },
  created() {
    this.setCurrentValue(this.value);
  },
  methods: {
    stepPrecision(step) {
      const _step = step.toString();
      const _position = _step.indexOf(".");
      if (_position == -1) return 0;
      return _step.length - _position - 1;
    },
    toPrecision(num, precisionPow) {
      return parseFloat(Math.round(num * precisionPow) / precisionPow);
    },
    _increase(value, step) {
      const precision_s = this.stepPrecision(step);
      const precision_v = this.stepPrecision(value);
      const precision = Math.max(precision_s, precision_v);
      const precisionPow = Math.pow(10, precision);
      return this.toPrecision(
        (value * precisionPow + step * precisionPow) / precisionPow,
        precisionPow
      );
    },
    _decrease(value, step) {
      const precision_s = this.stepPrecision(step);
      const precision_v = this.stepPrecision(value);
      const precision = Math.max(precision_s, precision_v);
      const precisionPow = Math.pow(10, precision);
      return this.toPrecision(
        (value * precisionPow - step * precisionPow) / precisionPow,
        precisionPow
      );
    },
    increase() {
      if (this.disabled || this.maxDisabled) return;
      const value =
        this.value === undefined
          ? 0
          : this.value >= this.min
          ? this.value
          : this.min;
      const newValue = this._increase(value, this.step);
      this.setCurrentValue(newValue);
    },
    decrease() {
      if (this.disabled || this.minDisabled) return;
      const value = this.value || 0;
      const newValue = this._decrease(value, this.step);
      this.setCurrentValue(newValue);
    },
    setCurrentValue(newVal) {
      const oldVal = this.currentValue;
      if (newVal >= this.max) newVal = this.max;
      if (newVal <= this.min) newVal = this.min;
      if (oldVal === newVal) return;
      this.userInput = null;
      this.$emit("input", newVal);
      this.$emit("change", newVal, oldVal);
      this.currentValue = newVal;
    },
    handlerInput(value) {
      this.userInput = value;
    },
    handleBlur(event) {
      this.$emit("blur", event);
    },
    handleFocus(event) {
      this.$emit("focus", event);
    },
    handleInputChange(value) {
      const newVal = Number(value);
      this.setCurrentValue(newVal);
      this.userInput = null;
    },
  },
};
</script>
<style lang="less" scoped></style>
